package withLamportClock;

import java.io.*;
import java.net.*;
import java.util.Random;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

/**
 * This a philosopher !
 * It will be implemented with Lamport Logic Clocks and sockets !
 * @author jjeske
 *
 */
public class Philo implements Runnable{
	
	private int id; //id of the philosopher
	private static final int REQV = 21;
	private static final int REQP = 22;
	private static final int VOP  = 23;
	private static final int POP  = 24;
	private static final int ACK  = 25;	
	private int lc; //Logical Clock
	
	private int portaHelperLeft; //cada numero de porta é um Helper responsavel por um garfo!
	private int portaHelperRight;
	private int portaLocal; 
	
	static Logger logger = Logger.getLogger(Philo.class); //logger
	
	public Philo(int id) {
		super();
		this.id = id;
		this.lc = 1;
		this.portaHelperLeft = 6000 + this.id;
		this.portaHelperRight = 6000 + ((this.id + 1) % 5);
		this.portaLocal = 8000 + this.id;
		//TODO: precisam-se muitos helpers, pensar nisso depois !
		BasicConfigurator.configure(); 
        logger.setLevel(Level.INFO); 
	}
	
	@Override
	public void run() {
		Random rand = new Random();
		while(true){
			
			relForkLeft();
			relForkRight();
			
			System.out.println("(" + this.id + ", " + this.lc +"): THINKING");
			try {
				Thread.sleep((rand.nextInt(5))*1000);
			} catch (InterruptedException e) {
				logger.error(e.getMessage());
			} //thinking()			
			
			System.out.println("(" + this.id + ", " + this.lc +"): HUNGRY");
			
			getForkLeft();
			getForkRight();
						
			System.out.println("(" + this.id + ", " + this.lc +"): EATING");
			
			try {
				Thread.sleep((rand.nextInt(5))*1000);
			} catch (InterruptedException e) {
				logger.error(e.getMessage());
			} //eating				
		
		}
		
	}
	
	private void relForkRight() {		
		//TODO !!	sem[(this.id + 1) % 5].release();
			
	}
	
	
	private void relForkLeft() {		
		Message reqVMsg = new Message();
		reqVMsg.setPorta(this.portaLocal);
		reqVMsg.setMsg(REQV);
		reqVMsg.setTimeStamp(this.lc);
		while(!NetFunctions.sendSemop(this.portaHelperLeft,reqVMsg)){
			; //libera o semaphoro
		}
		reqVMsg = null;
		this.lc++;
		
		logger.info("philosopho: " + this.id + " mandou REQV");
		
		//sem[this.id].release();			
	}
	
	private void getForkRight(){
		//TODO!!! 		sem[(this.id + 1) % 5].acquire();
		//System.out.println("Philosoper_" + this.id + "has left fork");
	}
	
	private void getForkLeft() {		
		Message reqPMsg = new Message();
		reqPMsg.setPorta(this.portaLocal);
		reqPMsg.setMsg(REQP);
		reqPMsg.setTimeStamp(this.lc);
		while(!NetFunctions.sendSemop(this.portaHelperLeft, reqPMsg)){
			;
		}
		logger.info("philosopho: " + this.id + " mando REQP");
		
		this.lc++;
		
		int receivedLogicalClock = 0;
		while(receivedLogicalClock == 0){
			receivedLogicalClock = NetFunctions.receiveGo(this.portaLocal);
		}
		this.lc = Math.max(receivedLogicalClock, this.lc + 1);
		this.lc++;
		
		logger.info("philosopho: " + this.id + " recebeu GO");
				
		//System.out.println("Philosoper_" + this.id + "has right fork");
	}

		
}
